package com.example.lambda

import org.junit.Test


/**
 * 高阶函数
 * 说明：函数的参数或者返回值是函数，叫做高阶函数。
 * 函数的函数就是高阶函数；
 * 或者函数中有Lambda就是高阶函数
 *
 * @author zhouronghua
 * @time 2022/3/25 10:45 下午
 */
class HighFunctionTest {

    /**
     * 高阶函数应用
     *
     * return没有开放类型自动开放。
     */
    @Test
    fun testHighFunction() {
        // 方法变量methodX01，引用的函数类型(String) -> Int
        // 指向方法的引用 fun(str):Int
        val methodX01: (String) -> Int = fun(str): Int {
            // 需要写return，不是Lambda表达式，方法体需要写return
            return str.length
        }
        // 一般开发不会用 fun关键字 + 声明处
        println(methodX01("我是方法引用"))
        println("== 不使用fun关键字的Lambda引用 ==")
        // methodX02的类型是什么？(String)->Int
        // 背后的原理：生成Function1<String, Int>的接口
        /**
         * 函数式泛型，最多有22个参数。超过的话低版本会奔溃，高版本
         * 有保护机制
         * A function that takes 1 argument.
         * public interface Function1<in P1, out R> : Function<R> {
         *          Invokes the function with the specified argument.
         *  public operator fun invoke(p1: P1): R
         * }
         */
        val methodX02 = { str: String -> str.length }
        println("匿名函数调用 ${methodX02("匿名函数引用")}")
        println("== 使用Function接口声明函数的Lambda引用 ==")
        // 通过Function调用效果 匿名函数类型可以使用Function1接口替代，因为
        // 只有一个输入参数，因此使用Function1接口
        val methodX03: Function1<String, Int> = {
            it.length
        }
        println("Function调用调用 ${methodX03("匿名函数引用")}")
        // 调用高阶函数
        println("==测试调用高阶函数==")
        // 最后一个参数是Lambda，可以通过实现Lambda替换，成为尾随函数
        val r: String = show02(99) {
            println(it)
            "传入高阶函数的值是it $it"
        }
        println("高阶函数调用结果 $r")
        val r2: String = show01(100) {
            println(it)
            "fun传入高阶函数的值是it $it"
        }
        println("fun高阶函数调用结果 $r2")
        // 测试不同的lambda函数
        println("==测试lambda函数不同实现==")
        println("实现方式为计算三个整数的和")
        println(show03(10, 20, 30) { n1, n2, n3 ->
            n1 + n2 + n3
        })
        // 测试三个整数的积
        println("实现方式为计算三个整数的积")
        println(show03(2, 5, 10) { n1, n2, n3 ->
            n1 * n2 * n3
        })
        println("实现方式为计算三个整数的连除")
        // lambda多个参数可以采用(n1,n2,n3)表示，也可以直接使用参数之间逗号分割n1,n2,n3
        println(show03(100, 2, 5) { n1, n2, n3 ->
            n1 / n2 / n3
        })
        // 高阶函数测试
        println("==测试高阶函数复杂用法==")
        /**
         * fun4是一个函数引用，fun(n1: Int, n2: Int)是函数本身
         * (Int, Int) -> String: fun4函数的函数，以返回值返回了这个函数
         * { n1, n2 -> "$n1 加 $n2 的结果是${n1 + n2}" }，这个是匿名函数的实现，即fun4(n1: Int, n2: Int)的实现。
         * 从上可以看到，fun(n1: Int, n2: Int)中的这两个参数其实没有被使用到。
         */
        var fun4 =
            fun(n1: Int, n2: Int): (Int, Int) -> String = { n1, n2 -> "$n1 加 $n2 的结果是${n1 + n2}" }
        // 返回结果为lambda表达式的返回值
        println(fun4(10, 19)(100, 100))
        /**
         * fun5是一个函数引用，函数输入类型为String, 输出类型为一个函数类型  (String) -> (Boolean) -> (Int) -> (String) -> Int
         * 返回函数第二层嵌套：输入String，返回函数类型： (Boolean) -> (Int) -> (String) -> Int
         * 返回函数第三层嵌套：输入Boolean, 返回函数类型： (Int) -> (String) -> Int
         * 返回函数第四层嵌套：输入Int，返回函数(String) -> Int
         * 第五层函数嵌套：输入String, 返回Int
         */
        println("测试高级函数嵌套返回")
        val fun5: (String) -> (String) -> (Boolean) -> (Int) -> (String) -> Int =
            {
                // it对应的第一层函数返回类型是高阶函数： (String) -> (Boolean) -> (Int) -> (String) -> Int
                {
                    // 继续解析第二层函数：输入类型是String, 输出类型是高阶函数：(Boolean) -> (Int) -> (String) -> Int
                    {
                        // 实现第三层返回匿名函数：输入类型是Boolean，输出类型是高阶函数： (Int) -> (String) -> Int
                        {
                            // 实现第四层返回匿名函数：输入类型是Int，输出类型是函数： (String) -> Int
                            {
                                // 实现第五层函数，输入类型是String，返回值是Int
                                it.length * 2
                            }
                        }
                    }
                }
            }
        println(fun5("AAA")("BBB")(true)(10)("CCCC"))
        // 测试高阶函数应用
        println("==测试高阶函数使用==")
        val fun05 = loginEngine("李来", "1234") {
            if (it) println("登录成功, 恭喜")
            else println("登录失败，请重试")
        }
        // 测试泛型+扩展函数
        println("==测试扩展函数+泛型==")
        fun commonOK() = println("我是通用函数")
        val r1: Boolean = commonOK().myRunTest {
            // 泛型函数对谁进行扩展，就执行被扩展对象本身。
            // 此处对commonOK()进行扩展，持有commonOK()引用 commonOK()的返回类型是Unit
            println(this)
            true
        }
        "AAAAA".myRunTest {
            // 此处持有的this是字符串AAAA对象本省
            println(this)
            false
        }
        // 如果不使用模板方式T.()传递参数
        "AAAAA".myRunTest02 {
            // 这个对AAAA进行扩展，能够访问到AAAA.this
            println("不能获取到模板对象的本身 $this ")
            true
        }
    }

    /**
     * 高阶函数第二节课
     *
     */
    @Test
    fun testHighFunction02() {
        // T.() 里边
        // 第一个版本传入匿名函数参数，参数传入采用具名参数标记
        show("张三", lambda = {
            println("第一版本输出$it")
        })
        // 第二个版本：直接传入参数函数
        show("李四", {
            println("第二版本输出$it")
        })
        // 第三个版本：匿名函数参数采用尾随函数实现替代
        show("王五") {
            println("第三版本采用尾随函数 输出$it")
        }
        // 第四个版本：参数使用默认参数
        show {
            println("第四版本采用尾随函数+默认参数 输出$it")
        }
        // 测试多个lambda表达式参数
        println("==测试多个Lambda表达式==")
        show04(100, "黎明", lambda1 = {
            "接收到参数 $it"
        }, lambda2 = {
            println("lambda2很高心收到内容 $it")
        })
        show04(101, "刘德华", {
            "接收到参数 $it"
        }, {
            println("lambda2很高心收到内容 $it")
        })
        show04(102, "郭富城", {
            "接收到参数 $it"
        }) {
            println("lambda2很高心收到内容 $it")
        }
    }

    /**
     * 高阶函数方法引用
     */
    @Test
    fun testHighFunctionReference() {
        // 通过函数调动
        showAction {
            "输入参数的值 $it"
        }
        println("==测试采用方法引用==")
        // 对应的函数引用采用FunctionX表示
        val r1: Function1<Int, String> = ::actionImpl
        val r2: (Int) -> String = r1
        /**
         * Int.() == (Int)  扩展函数Int.()  Int.属于来源，会自动把来源作为第一个参数
         */
        val r3: Int.() -> String = r2
        showAction(r3)
        showAction(::actionImpl)
    }

    /**
     * action匿名函数的一个实现体
     */
    fun actionImpl(value: Int) = "接收到的值是 $value"

    fun showAction(action: (Int) -> String) {
        println(action(88))
    }

    /**
     * 含有两个Lambda表达式参数
     */
    fun show04(number: Int, str: String, lambda1: (Int) -> String, lambda2: (String) -> Unit) {
        println(lambda1(number))
        lambda2(str)
    }


    /**
     * 参数lambda是一个匿名函数
     */
    fun show(name: String = "小猪猪", lambda: (String) -> Unit) = lambda(name)

    /**
     * 采用泛型+扩展函数
     * mm的类型声明：T.() -> Boolean)
     * 扩展函数 T.() { 只有T.this对象本身 == T本身 }
     * Lambda表达式： (T) { 持有it == T本身 }
     * t.()的this，(t)的it有什么区别？
     * T.() 会给我们的 mm的lambda 就会产生 lambda实现体里面持有this == T 本身 == 123.T 所有this等于123
     *
     *
     * 难点：第一个this 调用端对象
     *          第二个this lambda表达式中的this
     */
    fun <T> T.myRunTest(mm: T.() -> Boolean) = mm()

    /**
     * 采用泛型+扩展函数
     * myRunTest02中的mm，是一个参数，对应参数类型是() -> Boolean，
     * T.myRunTest02定义的模板方法是：执行传入的函数方法体:mm()，mm.invoke()
     * 因为定义mm函数时，没有对模板变量的引用，所以外部使用lambda方法体中不能使用this
     *
     */
    fun <T> T.myRunTest02(mm: () -> Boolean) = mm()

    /**
     * 用户登录判断
     */
    fun loginEngine(userName: String, userPwd: String, responseResult: (Boolean) -> Unit) {
        responseResult(userName == "Joe" && userPwd == "!#232")
    }

    /**
     * 简单的高阶函数
     * show01：函数名
     * lambda： lambda表达式参数。对应匿名函数。(Int) -> String
     *
     */
    fun show01(number: Int, lambda: (Int) -> String): String {
        // fun函数体内的返回值需要使用return返回，且返回值需要显示声明类型
        return lambda.invoke(number)
    }

    /**
     * 简单的高阶函数
     * 功能效果与show01相同
     * show02：函数名
     * lambda： lambda表达式参数。对应匿名函数。(Int) -> String
     * show02的返回值为函数体的返回值类型，即lambda表达式的返回值类型一致
     *
     */
    fun show02(number: Int, lambda: (Int) -> String) = lambda.invoke(number)

    /**
     * 如果带有{}，这种是函数表达式的实现方式，需要明确定义返回值类型，方法体内需要显式声明返回值,需要return 关键字
     * 声明匿名函数的类型：lambda: (Int, Int, Int) -> Int，相当于函数接口Function3
     * 用户可以自定义实现传入。
     */
    fun show03(number1: Int, number2: Int, number3: Int, lambda: (Int, Int, Int) -> Int): Int {
        return lambda.invoke(number1, number2, number3)
    }
}